﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;

namespace HexunMicrobloggingClientUI
{
    class RSSContentDistiller
    {
        public RSSContentDistiller(string rssUris)
        {
            InitRssUriArray(rssUris);
            QueryRssContentToQueue();
        }

        private void InitRssUriArray(string rssUrls)
        {
            rssUriArray = rssUrls.Split(new char[]{';'}, StringSplitOptions.RemoveEmptyEntries);
        }

        private const int CONTENT_LENGTH = 140;

        private string[] rssUriArray = null;

        private int curRssUriIndex = 0;

        private static List<DateTime> pubDateList = new List<DateTime>();

        private static Queue<string> contentQueue = new Queue<string>();

        public string GetContent()
        {
            int attempCount = 0;
            while (contentQueue.Count == 0)
            {
                if (attempCount == 10)
                {
                    Logger.GetLogger().Error("Can not Query Rss Content using current Rss Uris, use GUID instead of the conent.");
                    return Guid.NewGuid().ToString();
                }

                if (QueryRssContentToQueue())
                {
                    break;
                }
                else
                {
                    attempCount++;
                }
            }

            return contentQueue.Dequeue();
        }

        private bool QueryRssContentToQueue()
        {
            if (rssUriArray.Length == curRssUriIndex)
            {
                curRssUriIndex = 0;
            }

            string rssUri = rssUriArray[curRssUriIndex++];
            if (string.IsNullOrEmpty(rssUri) ||string.IsNullOrWhiteSpace(rssUri))
            {
                Logger.GetLogger().Error("RSS URL is null, can not query rss content.");
                return false;
            }
            
            Raccoom.Xml.ConcreteRssFactory rssFactory = new Raccoom.Xml.ConcreteRssFactory();
            Raccoom.Xml.IRssChannel channel = null;
            try
            {
                channel = rssFactory.GetFeed(new Uri(rssUri));
                foreach (Raccoom.Xml.RssItem item in channel.Items)
                {
                    DateTime pubDate = item.PubDate;
                    string itemTitle = item.Title;
                    string itemDescription = GetContentFromCDATA(item.Description);

                    if (!pubDateList.Contains(pubDate))
                    {
                        pubDateList.Add(pubDate);
                        AddTextToContentQueue(itemTitle);
                        AddTextToContentQueue(itemDescription);
                    }
                }

                return contentQueue.Count > 0;
            }
            catch (Exception ex)
            {
                Logger.GetLogger().Error(ex);
            }

            return false;
        }

        private string GetContentFromCDATA(string cdataContent)
        {
            string xmlContent = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
            xmlContent += "<DataContent>";
            xmlContent += cdataContent;
            xmlContent += "</DataContent>";

            XmlDocument doc = new XmlDocument();
            doc.LoadXml(xmlContent);

            string content = doc.SelectSingleNode("//DataContent").InnerText;
            if (HasHtmlTags(content))
            {
                content = RemoveHtmlTags(content);
            }

            return content;
        }

        private bool HasHtmlTags(string content)
        {
            List<string> htmlTags = new List<string>();
            htmlTags.Add("html");
            htmlTags.Add("head");
            htmlTags.Add("body");
            htmlTags.Add("title");
            htmlTags.Add("div");
            htmlTags.Add("img");
            htmlTags.Add("table");
            htmlTags.Add("span");

            foreach (string htmlTag in htmlTags)
            {
                if (content.Contains(htmlTag))
                {
                    return true;
                }
            }

            return false;
        }

        private string RemoveHtmlTags(string content)
        {
            bool existHtmlTag = content.Contains("<html") && content.Contains("</html>");
            bool existBodyTag = content.Contains("<body") && content.Contains("</body>");

            string html = string.Empty;
            if (!existHtmlTag)
            {
                html += "<html>";
            }
            if (!existBodyTag)
            {
                html += "<body>";
            }
            html += content;
            if (!existHtmlTag)
            {
                html += "</body>";
            }
            if (!existBodyTag)
            {
                html += "</html>";
            }
            
            try
            {
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(html);
                XmlNode body = doc.SelectSingleNode("//html/body");
                return body.InnerText;
            }
            catch (Exception ex)
            {
                Logger.GetLogger().Error(ex);
            }

            return content;
        }

        private void AddTextToContentQueue(string content)
        {
            if (string.IsNullOrEmpty(content) || string.IsNullOrWhiteSpace(content))
            {
                Logger.GetLogger().Error("Content is null, can not add to queue.");
                return;
            }

            if (content.Length <= CONTENT_LENGTH)
            {
                contentQueue.Enqueue(content);
            }
            else
            {
                string[] contentArray = GetShortContent(content);
                if (contentArray != null)
                {
                    foreach (string shortContent in contentArray)
                    {
                        contentQueue.Enqueue(shortContent);
                    }
                }
            }
        }

        private string[] GetShortContent(string longContent)
        {
            if (longContent.Length <= CONTENT_LENGTH)
            {
                return null;
            }

            List<string> contentList = new List<string>();
            int startIndex = 0;

            do
            {
                if ((longContent.Length - startIndex) < CONTENT_LENGTH)
                {
                    contentList.Add(longContent.Substring(startIndex));
                }
                else
                {
                    contentList.Add(longContent.Substring(startIndex, CONTENT_LENGTH));
                }

                startIndex += CONTENT_LENGTH;

            } while (longContent.Length > startIndex);

            if (contentList.Count == 0)
            {
                return null;
            }

            string[] contentArray = new string[contentList.Count];
            contentList.CopyTo(contentArray);
            return contentArray;
        }
    }

    public class Subscriber
    {
        private Subscriber(string rssUris) 
        {
            rssDistiller = new RSSContentDistiller(rssUris);
            RssDistillerID = Guid.NewGuid().ToString();
        }

        private static readonly object padlock = new object();

        private static Dictionary<int, Subscriber> _instanceDic = new Dictionary<int, Subscriber>();


        private RSSContentDistiller rssDistiller = null;

        public string RssDistillerID { get; set; }

        /* //多线程安全单例模式
        private static Subscriber _instance = null;
        public static Subscriber Instance
        {
            get
            {
                if (_instance == null)
                {
                    lock (padlock)
                    {
                        if (_instance == null)
                        {
                            _instance = new Subscriber();
                        }
                    }
                }

                return _instance;
            }
        }
        */

        public static Subscriber GetInstance(int index, string rssUris)
        {
            lock (padlock)
            {
                Subscriber subscriber;
                bool result = _instanceDic.TryGetValue(index, out subscriber);
                if (!result)
                {
                    subscriber = new Subscriber(rssUris);
                    _instanceDic.Add(index, subscriber);
                }

                return subscriber;
            }
        }

        public string GetRSSContent()
        {
            return rssDistiller.GetContent();
        }
    }
}
